/**
 * 
 */
package org.swing.utility.system.flatform;
import java.io.File;
import java.io.IOException;

import org.swing.utility.system.util.CommonUtils;
/**
 * @author Le Quynh Nhu
 *
 */
public class SystemUtility {

    /**
     * Whether or not the native libraries could be loaded.
     */
    private static final boolean isLoaded;

    static {
        boolean canLoad;
        try {
            // Only load the library on systems where we've made it.
            if (OSUtils.isWindows()) {
                if (OSUtils.isGoodWindows()) {
                    System.loadLibrary("SystemUtilities");
                } else {
                    System.loadLibrary("SystemUtilitiesA");
                }
            } else if (OSUtils.isMacOSX()) {
                System.loadLibrary("SystemUtilities");
            }

            canLoad = true;
        } catch (UnsatisfiedLinkError noGo) {
            canLoad = false;
        }
        isLoaded = canLoad;
    }

    private SystemUtility() {
    }

    /** Identifies a special folder in the platform's shell. */
    public static enum SpecialLocations {
        HOME("Home"), DOCUMENTS("Documents"), MUSIC("Music"), APPLICATION_DATA("ApplicationData"), DESKTOP("Desktop"), START_MENU(
                "StartMenu"), START_MENU_PROGRAMS("StartMenuPrograms"), START_MENU_STARTUP("StartMenuStartup");

        private final String name;

        SpecialLocations(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        /**
         * Parse the text of a SpecialLocations identifier into that identifier.
         * 
         * @param name
         *            A String name, like "Documents".
         * @return The matching SpecialLocations identifier, like
         *         SpecialLocations.DOCUMENTS. null if no match.
         */
        public static SpecialLocations parse(String name) {
            for (SpecialLocations location : SpecialLocations.values())
                if (location.getName().equals(name))
                    return location;
            return null;
        }
    }

    /**
     * Gets the absolute path to a special folder in the platform's shell.
     * 
     * The returned path is specific to the current user, and current to how the
     * user has customized it. Here are the given special folder names and
     * example return paths on Windows XP:
     * 
     * <pre>
     * Home              C:\Documents and Settings\UserName
     * Documents         C:\Documents and Settings\UserName\My Documents
     * Desktop           C:\Documents and Settings\UserName\Desktop
     * ApplicationData   C:\Documents and Settings\UserName\Application Data
     * StartMenu         C:\Documents and Settings\UserName\Start Menu
     * StartMenuPrograms C:\Documents and Settings\UserName\Start Menu\Programs
     * StartMenuStartup  C:\Documents and Settings\UserName\Start Menu\Programs\Startup
     * </pre>
     * 
     * Home, Documents, and Desktop work on Windows, Mac, and Linux, the others
     * are Windows-only.
     * 
     * On Linux, this method sticks "Documents" and "Desktop" on the end of the
     * user's home directory. This will always produce a usable folder. In most
     * distributions, the window manager uses these folders as well. This method
     * does not guarantee Linux has a window manager configured to those
     * folders, however.
     * 
     * @param location
     *            A special folder identifier
     * @return The path to that folder, or null on not supported
     */
    public static File getSpecialPath(SpecialLocations location) {
        if (OSUtils.isWindows()) {
            if (location == SpecialLocations.HOME) {
                return CommonUtils.getUserHomeDir();
            } else {
                if (isLoaded) {
                    try {
                        String path = getSpecialPathNative(location.getName());
                        if (!path.equals(""))
                            return new File(path);
                        else
                            return null;
                    } catch (UnsatisfiedLinkError error) {
                        // this
                        // class
                        // still
                        // org.limewire.util.SystemUtils?
                        // If
                        // not,
                        // SystemUtils.dll
                        // needs
                        // to
                        // change.
                        return null;
                    }
                } else {
                    return null;
                }
            }
        } else if (OSUtils.isPOSIX()) { // Stick "Documents" and "Desktop" on
            // the user's Mac or Linux home
            // directory
            if (location == SpecialLocations.HOME) {
                return CommonUtils.getUserHomeDir();
            } else if (location == SpecialLocations.DOCUMENTS) {
                return new File(CommonUtils.getUserHomeDir(), location.getName());
            } else if (location == SpecialLocations.DESKTOP) {
                return new File(CommonUtils.getUserHomeDir(), location.getName());
            } else if (location == SpecialLocations.MUSIC) {
                return new File(CommonUtils.getUserHomeDir(), location.getName());
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Moves a file to the platform-specific trash can or recycle bin.
     * 
     * @param file
     *            The file to trash
     * @return True on success
     */
    public static boolean recycle(File file) {
        if (OSUtils.isWindows() && isLoaded) {

            // Get the path to the file
            String path = null;
            try {
                path = file.getCanonicalPath();
            } catch (IOException err) {
                path = file.getAbsolutePath();
            }

            // Use native code to move the file at that path to the recycle bin
            return recycleNative(path);

        } else {
            return false;
        }
    }

    /**
     * Reads a text value stored in the Windows Registry.
     * 
     * @param root
     *            The name of the root registry key, like "HKEY_LOCAL_MACHINE"
     * @param path
     *            The path to the registry key with backslashes as separators,
     *            like "Software\\Microsoft\\Windows"
     * @param name
     *            The name of the variable within that key, or blank to access
     *            the key's default value
     * @return The text value stored there or blank on error
     */
    public static String registryReadText(String root, String path, String name) throws IOException {
        if (OSUtils.isWindows() && isLoaded)
            return registryReadTextNative(root, path, name);
        throw new IOException(" not supported ");
    }

    /**
     * Runs a path using the default program on the native platform.
     * 
     * Given a path to a program, runs that program. Given a path to a document,
     * opens it in the default program for that kind of document. Given a path
     * to a folder, opens it in the shell.
     * 
     * This method returns immediately, not later after the program exits. On
     * Windows, this method does the same thing as Start, Run.
     * 
     * @param path
     *            The complete path to run, like "C:\folder\file.ext"
     * @return 0, in place of the process exit code
     */
    public static int openFile(String path) throws IOException {
        if (OSUtils.isWindows() && isLoaded) {
            openFileNative(path);
            return 0; // program's running, no way to get exit code.
        }

        throw new IOException("native code not linked");
    }

    /**
     * Runs a path using the default program on the native platform.
     * 
     * Given a path to a program, runs that program. Given a path to a document,
     * opens it in the default program for that kind of document. Given a path
     * to a folder, opens it in the shell.
     * 
     * Note: this method accepts a parameter list thus should be generally used
     * with executable files
     * 
     * This method returns immediately, not later after the program exits. On
     * Windows, this method does the same thing as Start, Run.
     * 
     * @param path
     *            The complete path to run, like "C:\folder\file.ext"
     * @param path
     *            The list of parameters to pass to the file
     * @return 0, in place of the process exit code
     */
    public static int openFile(String path, String params) throws IOException {
        if (OSUtils.isWindows() && isLoaded) {
            openFileParamsNative(path, params);
            return 0; // program's running, no way to get exit code.
        }

        throw new IOException("native code not linked");
    }

    /**
     * Opens a Web address using the default browser on the native platform.
     * 
     * This method returns immediately, not later after the browser exits. On
     * Windows, this method does the same thing as Start, Run.
     * 
     * @param url
     *            The Web address to open, like "http://www.limewire.com/"
     * @return 0, in place of the process exit code
     */
    public static int openURL(String url) throws IOException {
        if (OSUtils.isWindows() && isLoaded) {
            openURLNative(url);
            return 0; // program's still running, no way of getting an exit
            // code.
        }

        throw new IOException("native code not linked");
    }

    /**
     * This launches a process with elevated permissions. It should only be used
     * for Windows 7 (and possibly vista) b/c permissions are only an issue on
     * these platforms. And Microsoft implemented this functionality in a
     * backwards compatible way that nonetheless causes some funny behavior on
     * Windows XP.
     * 
     * @param pathToExecutable
     *            -- the executable to start with elevated permissions
     * @return 0 if successful
     * @throws IOException
     */
    public static int launchProcessWithElevatedPermissions(File pathToExecutable) throws IOException {
        if (OSUtils.isWindows() && isLoaded) {
            startProcessWithElevatedPermissionsNative(pathToExecutable.getAbsolutePath());
            return 0; // program's still running, no way of getting an exit
            // code.
        }

        throw new IOException("native code not linked");
    }

    private static native String getSpecialPathNative(String name);

    private static native boolean recycleNative(String path);

    private static native String registryReadTextNative(String root, String path, String name) throws IOException;

    private static native void openFileNative(String path);

    private static native void openFileParamsNative(String path, String params);

    private static native void openURLNative(String url);

    private static native void startProcessWithElevatedPermissionsNative(String pathToExecutable);
}